/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012-2013 Oliver Katz */

#include "arch.h"
#include "../common/debug.h"

#include "modules/mod_argbuf.h"
#include "modules/mod_codeserver.h"
#include "modules/mod_bus.h"
#include "modules/mod_log.h"
#include "modules/mod_share_client.h"
#include "modules/mod_share_server.h"
#include "modules/mod_ipc_client.h"
#include "modules/mod_ipc_server.h"
#include "modules/mod_visual_script.h"

namespace sleepyface
{
	namespace arch
	{
		Architecture::Architecture(string n)
		{
			name = n;
		}

		string Architecture::getName()
		{
			return name;
		}

		string Architecture::load(ArchModule *m, vector<ModuleValue> data)
		{
			if (mods.find(m->getName()) != mods.end())
			{
				Z_WARNING("module " << m->getName() << " has already been loaded");
				return "";
			}
			mods[m->getName()] = shared_ptr<ArchModule>(m);
			ModuleSignal tmp = ModuleSignal(ArchModule::signalInitialize);
			for (vector<ModuleValue>::iterator i = data.begin(); i != data.end(); i++)
				tmp.append(*i);
			int e = 0;
			if ((e = mods[m->getName()]->signal(tmp, *this)) != 0)
			{
				Z_WARNING("cannot load module: " << m->getName() << " (" << e << ")");
				return "";
			}
			else
				return m->getName();
		}

		string Architecture::load(ArchModule *m)
		{
			vector<ModuleValue> data;
			return load(m, data);
		}

		string Architecture::load(ArchModule *m, ModuleValue v0)
		{
			vector<ModuleValue> data;
			data.push_back(v0);
			return load(m, data);
		}

		int Architecture::loadRequiredInternals()
		{
			load(new modules::ArgumentBuffer());
			load(new modules::CodeServer());
		}

		int Architecture::loadOptionalInternals(int argc, char *argv[])
		{
			map<string, bool> loadCfg;
			loadCfg["bus"] = true;
			loadCfg["share-client"] = true;
			loadCfg["share-server"] = true;
			loadCfg["log"] = true;
			loadCfg["ipc-client"] = true;
			loadCfg["ipc-server"] = true;
			loadCfg["visual-script"] = true;

			map<string, bool> verboseCfg;
			for (map<string, bool>::iterator i = loadCfg.begin(); i != loadCfg.end(); i++)
				verboseCfg[i->first] = false;

			for (int i = 0; i < argc; i++)
			{
				if (string(argv[i]).find("--arch-module:") != string::npos)
				{
					string opt = string(argv[i]).substr(string(argv[i]).find(":")+1);
					loadCfg[opt.substr(0, opt.find("="))] = (opt.substr(opt.find("=")+1).compare("1") == 0);
				}
				else if (string(argv[i]).find("--arch-module-verbose:") != string::npos)
				{
					string opt = string(argv[i]).substr(string(argv[i]).find(":")+1);
					verboseCfg[opt.substr(0, opt.find("="))] = (opt.substr(opt.find("=")+1).compare("1") == 0);
				}
			}

			if (loadCfg["log"])
				load(new modules::Log());

			if (loadCfg["bus"])
				load(new modules::Bus());

			if (loadCfg["share-client"])
				load(new modules::ShareClient());
			
			if (loadCfg["share-server"])
				load(new modules::ShareServer());

			if (loadCfg["ipc-client"])
				load(new modules::IPCClient());

			if (loadCfg["ipc-server"])
				load(new modules::IPCServer());

			if (loadCfg["visual-script"])
			{
				ModuleSignal s = ModuleSignal(modules::IPCClient::signalConnect);
				s.append(ModuleValue(ModuleValue::_string, "visual-server"));
				s.append(ModuleValue(ModuleValue::_string, "127.0.0.1"));
				s.append(ModuleValue(ModuleValue::_int, 8952));
				module("ipc-client")->signal(s, *this);
				load(new modules::VisualScript());
			}

			for (map<string, bool>::iterator i = verboseCfg.begin(); i != verboseCfg.end(); i++)
				if (i->second)
					module(i->first)->enableVerbose();

			cliArgs(argc, argv);
		}

		bool Architecture::isLoaded(string n)
		{
			return (mods.find(n) != mods.end());
		}

		int Architecture::unload(string n)
		{
			if (mods.find(n) == mods.end())
				return 1;
			ModuleSignal tmp = ModuleSignal(ArchModule::signalUninitialize);
			int e = 0;
			if ((e = mods[n]->signal(tmp, *this)) != 0)
			{
				Z_WARNING("cannot unload module: " << n << " (" << e << ")");
				return 2;
			}
			mods.erase(n);
			return 0;
		}

		int Architecture::size()
		{
			return mods.size();
		}

		vector<string> Architecture::list()
		{
			vector<string> rtn;
			for (map<string, shared_ptr<ArchModule> >::iterator i = mods.begin(); i != mods.end(); i++)
				rtn.push_back(i->first);
			return rtn;
		}

		ArchModule *Architecture::module(string n)
		{
			if (mods.find(n) == mods.end())
			{
				Z_WARNING("cannot retrieve module reference: " << n);
				for (map<string, shared_ptr<ArchModule> >::iterator i = mods.begin(); i != mods.end(); i++)
					Z_NOTE("module", i->first);
			}
			return mods[n].get();
		}

		int Architecture::cliArgs(int c, char *v[])
		{
			argc = c;
			argv = v;

			for (map<string, shared_ptr<ArchModule> >::iterator i = mods.begin(); i != mods.end(); i++)
			{
				int e = 0;
				if ((e = i->second->signal(ModuleSignal(ArchModule::signalCommandLineArguments), *this)) != 0)
				{
					Z_WARNING("cannot handle command line arguments in module: " << i->first << " (" << e << ")");
					return 1;
				}
			}

			return 0;
		}

		int Architecture::getArgc()
		{
			return argc;
		}

		char **Architecture::getArgv()
		{
			return argv;
		}

		int Architecture::getMemoryUsage()
		{
			stringstream ss;
			ss << "/proc/" << getpid() << "/status";
			z_freader reader = z_freader_init((char *)ss.str().c_str(), Z_FIO_BUFSIZE_DEFAULT);
			if (!z_freader_ok(&reader))
			{
				Z_WARNING("cannot access external memory usage");
				return -1;
			}
			z_data buf = z_freader_read(&reader);
			z_freader_release(reader);
			string status = string((const char *)z_data_src_get(&buf));
			opal::Lexer l = opal::Lexer("\n", "\n", "", "");
			vector<opal::Token> tmp = l.lex(status, "-process-status-");
			for (vector<opal::Token>::iterator i = tmp.begin(); i != tmp.end(); i++)
			{
				if (i->get().find("VmSize:") != string::npos)
				{
					string s = i->get().substr(string("VmSize:").size());
					s = s.substr(0, s.size()-2);
					return atoi(s.c_str());
				}
			}

			return -1;
		}
	}
}
